#include "pdxcxx.h"

#include <algorithm>
#include <cassert>
#include <functional>
#include <iomanip>
#include <iostream>

#include "utils.h"

namespace pdxcxx
{
	Diagnostic::IDLevelMap Diagnostic::_levels;
	int Diagnostic::_numErrors;
	int Diagnostic::_numWarnings;

	void Diagnostic::report()
	{
		if (_level == Disabled) return;

		const char* suffix;
		if (_level == Error)
		{
			suffix = "E";
			_numErrors += 1;
		}
		else
		{
			assert(_level == Warning);
			suffix = "W";
			_numWarnings += 1;
		}

		std::cout << _stream.str() << " (" << suffix << std::setfill('0') << std::setw(4) << (unsigned)_id << ")\n";
		_stream.clear();

		_level = Disabled; //can only report once for now
	}

	void Diagnostic::setLevel(diagnostic::ID id, Level level)
	{
		_levels[id] = level;
	}

	WholeProgram::WholeProgram(ClangParser& parser) :
		_parser(parser)
	{
	}

	clang::SourceManager& WholeProgram::getSourceManager() const
	{
		return _parser.getSourceManager();
	}

	void WholeProgram::addDefinition(const clang::FunctionDecl* def)
	{
		assert(def->isThisDeclarationADefinition());
		auto id = mangle(def, &_parser.getASTContext());

		_definitions[id] = def;
	}

	const clang::FunctionDecl* WholeProgram::findDefinition(const clang::FunctionDecl* decl) const
	{
		auto id = mangle(decl, &_parser.getASTContext());
		auto it = _definitions.find(id);
		if (it != _definitions.end()) return it->second;
		return nullptr;
	}

	void IAnalysis::run(const IAnalysis::MatchResult& result)
	{
		matchAST(result);
	}
} //namespace pdxcxx

static void setDefaultDiagnosticLevels()
{
	using namespace pdxcxx;

	Diagnostic::setLevel(diagnostic::UndefinedSoCantAnalyze, Diagnostic::Warning);
}

int main(int argc, const char **argv)
{
	using namespace pdxcxx;

	if (argc <= 1)
	{
		std::cout << "Must specify at least one source file to analyze\n";
		return 1;
	}

	setDefaultDiagnosticLevels();

	ClangParser parser;
	WholeProgram program(parser);
	clang::ast_matchers::MatchFinder finder;
	AnalysisList analysisList(program, finder);
	analysisList.listAll();

	for (int i = 1; i < argc; ++i)
	{
		auto astContext = parser.parseAST(argv[i]);
		program.addAST(std::move(astContext));
	}

	std::for_each(program.astBegin(), program.astEnd(), [&](const std::unique_ptr<clang::ASTContext>& ast)
	{
		finder.matchAST(*ast.get());
	});
	std::for_each(analysisList.analysisBegin(), analysisList.analysisEnd(),
		std::bind(&IAnalysis::runAnalysis, std::placeholders::_1));

	return 0;
}
